/**
 * 获取路由需要的权限
 * @param permissions
 * @param route
 * @returns {Permission}
 */
const getRoutePermission = (permissions, route) => permissions.find(item => item.id === route.meta.authority.permission)
    /**
     * 获取路由需要的角色
     * @param roles
     * @param route
     * @returns {Array[Role]}
     */
const getRouteRole = (roles, route) => {
        const requiredRoles = route.meta.authority.role
        return requiredRoles ? roles.filter(item => requiredRoles.findIndex(required => required === item.id) !== -1) : []
    }
    /**
     * 判断是否已为方法注入权限认证
     * @param method
     * @returns {boolean}
     */
const hasInjected = (method) => method.toString().indexOf('//--auth-inject') !== -1

/**
 * 操作权限校验
 * @param authConfig
 * @param permission
 * @param role
 * @param permissions
 * @param roles
 * @returns {boolean}
 */
const auth = function(authConfig, permission, role, permissions, roles) {
    const { check, type } = authConfig
    if (check && typeof check === 'function') {
        return check.apply(this, [permission, role, permissions, roles])
    }
    if (type === 'permission') {
        return checkFromPermission(check, permission)
    } else if (type === 'role') {
        return checkFromRoles(check, role)
    } else {
        return checkFromPermission(check, permission) || checkFromRoles(check, role)
    }
}

/**
 * 检查权限是否有操作权限
 * @param check 需要检查的操作权限
 * @param permission 权限
 * @returns {boolean}
 */
const checkFromPermission = function(check, permission) {
    return permission && permission.operation && permission.operation.indexOf(check) !== -1
}

/**
 * 检查 roles 是否有操作权限
 * @param check 需要检查的操作权限
 * @param roles 角色数组
 * @returns {boolean}
 */
const checkFromRoles = function(check, roles) {
    if (!roles) {
        return false
    }
    for (let role of roles) {
        const { operation } = role
        if (operation && operation.indexOf(check) !== -1) {
            return true
        }
    }
    return false
}

const checkInject = function(el, binding, vnode) {
    const type = binding.arg
    const check = binding.value
    const instance = vnode.context
    const $auth = instance.$auth
    if (!$auth || !$auth(check, type)) {
        addDisabled(el)
    } else {
        removeDisabled(el)
    }
}

const addDisabled = function(el) {
    if (el.tagName === 'BUTTON') {
        el.disabled = true
    } else {
        el.classList.add('disabled')
    }
    el.setAttribute('title', '无此权限')
}

const removeDisabled = function(el) {
    el.disabled = false
    el.classList.remove('disabled')
    el.removeAttribute('title')
}

const AuthorityPlugin = {
    install(Vue) {
        Vue.directive('auth', {
            bind(el, binding, vnode) {
                setTimeout(() => checkInject(el, binding, vnode), 10)
            },
            componentUpdated(el, binding, vnode) {
                setTimeout(() => checkInject(el, binding, vnode), 10)
            },
            unbind(el) {
                removeDisabled(el)
            }
        })
        Vue.mixin({
            beforeCreate() {
                if (this.$options.authorize) {
                    const authorize = this.$options.authorize
                    Object.keys(authorize).forEach(key => {
                        if (this.$options.methods[key]) {
                            const method = this.$options.methods[key]
                            if (!hasInjected(method)) {
                                let authConfig = authorize[key]
                                authConfig = (typeof authConfig === 'string') ? { check: authConfig } : authConfig
                                const { check, type, onFailure } = authConfig
                                this.$options.methods[key] = function() {
                                    //--auth-inject
                                    if (this.$auth(check, type)) {
                                        return method.apply(this, arguments)
                                    } else {
                                        if (onFailure && typeof onFailure === 'function') {
                                            this[`$${check}Failure`] = onFailure
                                            return this[`$${check}Failure`](check)
                                        } else {
                                            this.$message.error(`对不起，您没有操作权限：${check}`)
                                        }
                                        return 0
                                    }
                                }
                            }
                        }
                    })
                }
            },
            methods: {
                /**
                 * 操作权限校验
                 * @param check 需要校验的操作名
                 * @param type 校验类型，通过 permission 校验，还是通过 role 校验。
                 * 如未设置，则自动识别，如匹配到当前路由 permission 则 type = permission，否则 type = role
                 * @returns {boolean} 是否校验通过
                 */
                $auth(check, type) {
                    const permissions = this.$store.getters['account/permissions']
                    const roles = this.$store.getters['account/roles']
                    const permission = getRoutePermission(permissions, this.$route)
                    const role = getRouteRole(roles, this.$route)
                    return auth.apply(this, [{ check, type }, permission, role, permissions, roles])
                }
            }
        })
    }
}

export default AuthorityPlugin